package com.xiaotie.colorPicker

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.annotation.ColorInt
import java.lang.Math.toRadians
import kotlin.math.sqrt
import kotlin.math.tan


class RoundColorPaletteHSV360 constructor(context: Context, attrs: AttributeSet?, defStyleAttr:Int): View(context,attrs,defStyleAttr) {

    constructor(context: Context):this(context,null,0)

    constructor(context: Context,attrs: AttributeSet?):this(context,attrs,0)

    //取色范围半径
    var radius:Float = 0f
        private set(value){
            field = value-stroke-gap-colorRadius
        }

    //取色圆半径
    var colorRadius = 50f
        set(value){
            if (value >= 0)
                field = value
        }

    //取色圆边框半径
    var colorStroke = 8f
        set(value){
            if (value >= 0)
                field = value
        }

    //取色圆边框颜色
    var colorStrokeColor = Color.BLACK

    //取色颜色
    var color = Color.WHITE

    //边框半径
    var stroke = 24f
        set(value){
            if (value >= 0)
                field = value
        }

    //边框颜色
    var strokeColor = Color.YELLOW

    //间隙半径
    var gap = 4f
        set(value){
            if (value >= 0)
                field = value
        }

    var isOutOfBounds:Boolean = false

    private val paint = Paint()
    private val colors1:IntArray
    private val positions1 :FloatArray
    private val colors2:IntArray
    private val positions2 :FloatArray
    private var xColor: Float = 0f
    private var yColor: Float = 0f
    private var colorChangeCallBack: ColorChangeCallBack? = null

    init {
//        <declare-styleable name="RoundColorPaletteHSV360">
//        <attr name="colorRadius" format="dimension"/>
//        <attr name="colorStroke" format="dimension"/>
//        <attr name="gap" format="dimension"/>
//        <attr name="stroke" format="dimension"/>
//        <attr name="colorStrokeColor" format="color"/>
//        <attr name="strokeColor" format="color"/>
//        <attr name="isOutOfBounds" format="boolean"/>
//        </declare-styleable>
        val  typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundColorPaletteHSV360)
        colorRadius = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_colorRadius,50f)
        colorStroke = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_colorStroke,8f)
        gap = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_gap,4f)
        stroke = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_stroke,24f)
        colorStrokeColor = typedArray.getColor(R.styleable.RoundColorPaletteHSV360_colorStrokeColor,Color.BLACK)
        strokeColor = typedArray.getColor(R.styleable.RoundColorPaletteHSV360_strokeColor,Color.YELLOW)
        isOutOfBounds = typedArray.getBoolean(R.styleable.RoundColorPaletteHSV360_isOutOfBounds,false)
        typedArray.recycle()

        val colorCount1 = 360
        val colorCount2 = 255
        val colorAngleStep = 360 / colorCount1
        positions1 = FloatArray(colorCount1+1){i-> i/(colorCount1*1f)}
        var hsv = floatArrayOf(0f, 1f, 1f)
        colors1 = IntArray(colorCount1+1){ i->
            hsv[0] = 360 - i * colorAngleStep % 360f
            Color.HSVToColor(hsv)
        }
        hsv = floatArrayOf(0f, 0f, 1f)
        positions2 = FloatArray(colorCount2+1){i-> i/(colorCount2*1f)}
        colors2 = IntArray(colorCount2+1){ i->
            Color.HSVToColor(255 * (colorCount2 -i) / colorCount2 , hsv)
        }

    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = MeasureSpec.getSize(widthMeasureSpec)/2f
        val height = MeasureSpec.getSize(heightMeasureSpec)/2f
        radius = if(width-height<0) width else height
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas?) {
        createColorWheel(canvas)
        createColorRadius(canvas,xColor,yColor)
        super.onDraw(canvas)
    }

    //色盘
    private fun createColorWheel(canvas: Canvas?){
        paint.reset()
        paint.isAntiAlias = true
        paint.shader = ComposeShader(
            SweepGradient(
                width / 2f,
                height / 2f,
                colors1,
                positions1
            ), RadialGradient(
                width / 2f,
                height / 2f,
                radius-colorRadius,
                colors2,
                positions2,
                Shader.TileMode.CLAMP
            ),  PorterDuff.Mode.SRC_OVER)
        canvas?.drawCircle(width / 2f,height / 2f,radius,paint)
        paint.shader = null
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = stroke
        paint.color = strokeColor
        canvas?.drawCircle(width / 2f,height / 2f,radius+gap+stroke/2,paint)
    }

    //取色圆
    private fun createColorRadius(canvas: Canvas?, rx: Float, ry: Float){
        var x = rx
        var y = ry
        if(x==0f || y==0f ){
            x = width / 2f
            y = height / 2f
        }
        paint.reset()
        paint.isAntiAlias = true
        paint.color = color
        canvas?.drawCircle(x,y,colorRadius,paint)
        paint.style = Paint.Style.STROKE
        paint.color = colorStrokeColor
        paint.strokeWidth = colorStroke
        canvas?.drawCircle(x,y,colorRadius+colorStroke/2,paint)
    }

    fun setColorChangeCallBack(colorChangeCallBack: ColorChangeCallBack){
        this.colorChangeCallBack = colorChangeCallBack
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        event?.let {
            val pointToCircle = pointToCircle(it.x, it.y)
            if( pointToCircle <= radius - if(isOutOfBounds) 0f else (colorRadius-colorStroke)){
                xColor =  it.x
                yColor =  it.y
                color =  Color.HSVToColor(floatArrayOf((angle(it.x,it.y)),pointToCircle/radius,1f))
                colorChangeCallBack?.onChange(color)
            }else{
                findPoint( it.x, it.y)
            }
            invalidate()
        }
        return true
    }

    //点到圆心距离
    private fun pointToCircle(x: Float = width / 2f, y: Float = height / 2f ) =
        sqrt((x - width / 2f)*(x - width / 2f) + (y-height / 2f)*(y-height / 2f))

    //离目标最近的点 x²+y²=r² 和 ax+by=0(一般式) 的解方程
    private fun findPoint(x1: Float = width / 2f, y1: Float = height / 2f ){
//      直线一般方程
//      以圆心为坐标0，0 重新计算点（a，b）坐标
        val a = y1 - height / 2f
        val b = x1 - width / 2f
        val r = radius - if(isOutOfBounds) 0f else (colorRadius-colorStroke)
        //r^2/((b/a)^2+1)的开平方
        yColor = sqrt( (r * r) / ((b / a) * (b / a) + 1))
        //判断开平方的正负值
        if(a<0) yColor = -yColor
        xColor =  (b*yColor)/a + width / 2f
        yColor += height / 2f
        color = Color.HSVToColor(floatArrayOf((angle(xColor,yColor)),1f,1f))
        colorChangeCallBack?.onChange(color)

    }

    //角度
    private fun angle(x: Float = width / 2f, y: Float = height / 2f ):Float{
        var angdeg: Int

        //特殊角度, 与x轴垂直不存在斜率
        if(x - width / 2f == 0f && y - height / 2f < 0){
            angdeg = 90
        }else{
            //到圆心的斜率
            val k = ((y-height / 2f)*(y-height / 2f))/((x - width / 2f)*(x - width / 2f))
            //二分法
            var min = 0.00
            var max = 90.00
            while(max-min>1){
                val deg = min + (max - min) / 2
                if(k>tan(toRadians(deg))) min = deg else max = deg
            }
            angdeg = (max-1).toInt()
        }

        if((x - width / 2f <= 0f && y - height / 2f <= 0f)) {//第二象限 90~180
            angdeg = 180 - angdeg
        }else if((x - width / 2f <= 0f && y - height / 2f >= 0f)) {//第三象限 180~270
            angdeg += 180
        }else if((x - width / 2f >= 0f && y - height / 2f >= 0f)) {//第四象限 270~360
            angdeg = 360 - angdeg
        }

        return angdeg.toFloat()
    }

    //根据颜色定位取色盘位置 x²+y²=r² 和 y=kx(点斜式) 的解方程
    private fun findColorPoint(color: Int, x: Float = width / 2f, y: Float = height / 2f) {
        val hsv = floatArrayOf(0f, 1f, 1f)
        Color.colorToHSV(color,hsv)
        var angle = hsv[0].toDouble()
        var r = radius - if(isOutOfBounds) 0f else (colorRadius-colorStroke)
            r *= hsv[1]
        when (angle) {
            90.0 -> {
                xColor = x
                yColor = y-r
            }
            180.0 -> {
                xColor = x-r
                yColor = y
            }
            270.0 -> {
                xColor = x
                yColor = y+r
            }
            360.0 -> {
                xColor = x+r
                yColor = y
            }
            else -> {
                if(angle>180) angle = 360-angle
                val k = tan(toRadians(angle))
                xColor = sqrt((r*r) / (k*k + 1)).toFloat()
                if(hsv[0].toDouble()<90 || hsv[0].toDouble()>270) xColor = -xColor
                yColor = if(hsv[0].toDouble()>180) y - (k * xColor).toFloat() else y + (k * xColor).toFloat()
                xColor = x - xColor
            }
        }
        colorChangeCallBack?.onChange(color)
        invalidate()
    }

    //设置颜色
    fun changeColor(color: Int) {
        findColorPoint(color)
        this.color = color
    }

    interface ColorChangeCallBack{
        fun onChange(@ColorInt color: Int)
    }
}